package org.eclipse.jdt.core.dom;

/**
 * @Author: Coderlyz
 * @Date: 2023/4/13 17:11
 */

import lombok.extern.slf4j.Slf4j;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation;
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;

@Slf4j
public final class AUtils {

    private static final IAnnotationBinding[] NO_ANNOTATIONS = new IAnnotationBinding[0];

    private AUtils() {
    }

    public static void mayTolerateMissingType(AST ast) {
        ast.getBindingResolver().lookupEnvironment().mayTolerateMissingType = true;
    }

    public static void cleanupEnvironment(AST ast) {
        ast.getBindingResolver().lookupEnvironment().nameEnvironment.cleanup();
    }


    public static ITypeBinding resolveType(AST ast, String name) {
        try {
            BindingResolver bindingResolver = ast.getBindingResolver();
            ReferenceBinding referenceBinding = bindingResolver
                    .lookupEnvironment()
                    .getType(CharOperation.splitOn('.', name.toCharArray()));
            return bindingResolver.getTypeBinding(referenceBinding);
        } catch (Exception e) {
            // exception on ECJ side when trying to resolve a Type, recover on null type
            log.error(String.format("ECJ Unable to resolve type %s", name), e);
            return null;
        }
    }

    public static IAnnotationBinding[] resolvePackageAnnotations(AST ast, String packageName) {
        // See org.eclipse.jdt.core.dom.PackageBinding#getAnnotations()
        BindingResolver bindingResolver = ast.getBindingResolver();
        LookupEnvironment lookupEnvironment = bindingResolver.lookupEnvironment();
        NameEnvironmentAnswer answer = lookupEnvironment.nameEnvironment.findType(
                TypeConstants.PACKAGE_INFO_NAME,
                CharOperation.splitOn('.', packageName.toCharArray())
        );
        if (answer == null) {
            return NO_ANNOTATIONS;
        }
        IBinaryType type = answer.getBinaryType();
        if (type == null) {
            // Can happen for instance with ant, as ant only generates 'package-info.class'
            // when there is annotations in the package-info.java file.
            return NO_ANNOTATIONS;
        }
        IBinaryAnnotation[] binaryAnnotations = type.getAnnotations();
        AnnotationBinding[] binaryInstances =
                BinaryTypeBinding.createAnnotations(binaryAnnotations, lookupEnvironment, type.getMissingTypeNames());
        AnnotationBinding[] allInstances =
                AnnotationBinding.addStandardAnnotations(binaryInstances, type.getTagBits(), lookupEnvironment);

        IAnnotationBinding[] domInstances = new IAnnotationBinding[allInstances.length];
        for (int i = 0; i < allInstances.length; i++) {
            // FIXME can be null if annotation can not be resolved e.g. due to incomplete classpath
            domInstances[i] = bindingResolver.getAnnotationInstance(allInstances[i]);
        }
        return domInstances;
    }

    public static String signature(IMethodBinding methodBinding) {
        char[] signature = ((MethodBinding) methodBinding).binding.signature();
        return new String(signature);
    }

}

